package com.github.rxyor.common.support.hibernate.validate;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import com.github.rxyor.common.core.exception.IllegalParamException;
import com.github.rxyor.common.support.spring.context.SpringBeanHolder;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.HibernateValidator;

/**
 *<p>
 *
 *</p>
 *
 * @author liuyang
 * @date 2019/10/11 周五 15:51:00
 * @since 1.0.0
 */
@SuppressWarnings("all")
public class HibValidatorHelper {

    private final static String MULTI_ITEM_SPLIT_REGEX = ", [a-zA-Z]";
    private final static String TIP_SPLIT_REGEX = "[a-zA-Z0-9_$]: ";
    private static Validator validator;

    private HibValidatorHelper() {
    }

    /**
     *<p>
     *hibernate validator 异常信息解析
     *</p>
     *
     * @author liuyang
     * @date 2019-10-11 周五 16:14:59
     * @param e e
     * @return
     */
    public static List<String> parseException(ConstraintViolationException e) {
        if (e == null || e.getConstraintViolations() == null) {
            return new ArrayList<>(0);
        }
        Set<ConstraintViolation<?>> set = e.getConstraintViolations();
        return parseViolationSet(set);
    }

    public static <T> List<String> parseT(Set<ConstraintViolation<T>> set) {
        List<String> msgList = new ArrayList<>(8);
        set.iterator().forEachRemaining(
            violation -> msgList.add(violation.getMessage()));
        return msgList.stream().filter(StringUtils::isNotBlank).collect(Collectors.toList());
    }

    public static List<String> parseViolationSet(Set<ConstraintViolation<?>> set) {
        List<String> msgList = new ArrayList<>(8);
        set.iterator().forEachRemaining(
            violation -> msgList.add(violation.getMessage()));
        return msgList.stream().filter(StringUtils::isNotBlank).collect(Collectors.toList());
    }

    /**
     *<p>
     *hibernate validator 异常信息解析，示例错误提示信息如下：
     * "testManyValidator.age: 年龄应大于等于0, testManyValidator.name: 姓名不能为空"
     *</p>
     *
     * @author liuyang
     * @date 2019-10-11 周五 16:01:11
     * @param msg msg
     * @return
     */
    public static List<String> parseString(String msg) {
        if (StringUtils.isBlank(msg)) {
            return new ArrayList<>(0);
        }
        List<String> errorMsgTipList = new ArrayList<>(8);
        List<String> splitItemList = Lists.newArrayList(msg.split(MULTI_ITEM_SPLIT_REGEX));
        for (String item : splitItemList) {
            if (StringUtils.isBlank(item)) {
                continue;
            }
            List<String> validMsgSplitList = Lists.newArrayList(item.split(TIP_SPLIT_REGEX));
            if (validMsgSplitList.size() >= 2) {
                errorMsgTipList.add(validMsgSplitList.get(1));
            }
        }
        if (errorMsgTipList.size() == 0) {
            return Lists.newArrayList(msg);
        }
        return errorMsgTipList;
    }

    public static <T> void validate(T object, Class<?>... groups) {
        Preconditions.checkArgument(object != null, "param can't be null");

        List<String> msgList = parseT(getValidator().validate(object, groups));
        ifFailThrow(msgList);
    }

    public static <T> void validateProperty(T object, String propertyName, Class<?>... groups) {
        Preconditions.checkArgument(object != null, "param can't be null");

        List<String> msgList = parseT(getValidator().validateProperty(object, propertyName, groups));
        ifFailThrow(msgList);
    }

    public static <T> void validateValue(Class<T> beanType, String propertyName, Object value, Class<?>... groups) {
        Preconditions.checkArgument(value != null, "param can't be null");

        List<String> msgList = parseT(getValidator().validateValue(beanType, propertyName, value, groups));
        ifFailThrow(msgList);
    }

    private static void ifFailThrow(List<String> msgList) {
        if (msgList != null && msgList.size() > 0) {
            throw new IllegalParamException(Joiner.on(";").join(msgList));
        }
    }

    private static Validator getValidator() {
        if (validator != null) {
            return validator;
        }
        validator = SpringBeanHolder.getBean(Validator.class);
        if (validator == null) {
            validator = newValidator();
        }
        return validator;
    }

    private static Validator newValidator() {
        ValidatorFactory validatorFactory = Validation
            .byProvider(HibernateValidator.class)
            .configure()
            .failFast(true)
            .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}
